﻿/*****
 *  Copyright 2011, Kaven Gagné
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.IO;
using Knowledge.Resources;
using System.Threading;
using System.Timers;
using System.Windows.Media;


/***
 * Search - Find
 */


namespace Knowledge
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        // Instantiate the ButtonsManager
        ButtonsManager buttonsManager;
        System.Timers.Timer statusBarTimer;
        


        #region Constructor ---------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------
        public MainWindow()
        {
            this.InitializeComponent();

            // Insert code required on object creation below this point.
            if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
                // Init Buttons Manager
                buttonsManager = new ButtonsManager(this);

                Console.SetError(Console.Out);

                // RootViewModel will load the Root Folders in the TreeView
                // Set this viewModel as the DataContext
                RootViewModel viewModel = new RootViewModel();
                base.DataContext = viewModel;

                // Default Buttons State
                buttonsManager.changeStates(new int[] { /**/1, 1, 1, 2, /**/0, 0, 1, /**/1, 1, /**/1, 1, 1 });
            }
        }
        //---------------------------------------------------------------------------------------------------
        #endregion //Constructor ----------------------------------------------------------------------------




        #region statusBar Methods
        private void StatusBarMessage(string msg)
        {
            statusBarText.Content = msg;
            statusBarText.Visibility = Visibility.Visible;
            this.StatusBarReset();
        }
        private void StatusBarReset()
        {
            if (statusBarTimer != null)
                statusBarTimer.Stop();
            statusBarTimer = new System.Timers.Timer(8000);
            statusBarTimer.Elapsed += new ElapsedEventHandler(statusBarTimer_Elapsed);
            statusBarTimer.Start();
        }
        void statusBarTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            System.Threading.Thread thread = new System.Threading.Thread(
                new System.Threading.ThreadStart(
                    delegate()
                    {
                        statusBarText.Dispatcher.Invoke(
                            System.Windows.Threading.DispatcherPriority.SystemIdle,
                            new Action(
                                delegate()
                                {
                                    statusBarText.Visibility = Visibility.Collapsed;
                                    statusBarText.Content = "";
                                }
                        ));
                    }
            ));
            thread.Start();
        } 
        #endregion //statusBar Methods




        #region DescriptionCancel
        private void DescriptionCancel()
        {
            // Set ReadOnly to True
            descriptionTextBox.IsReadOnly = true;
            articlesTreeView.Focus();
            // Hides Cancel and Save buttons, shows Edit button
            buttonsManager.changeStates(new int[] { /**/-1, -1, -1, -1, /**/0, 0, 2, /**/-1, -1, /**/-1, -1, -1 });

            // Revert text to original from DB
            ViewModelBase article = (ViewModelBase)articlesTreeView.SelectedItem;
            int status;
            if (article is ArticleViewModel)
                status = ((ArticleViewModel)article).LoadDescription();
            else
                status = ((FolderViewModel)article).LoadDescription();

            if (status < (int)Utilities.errCode.NO_ERROR)
                this.StatusBarMessage("Unable to Retrieve Description from the Database. The Article or Folder may have been deleted.");
        }
        #endregion //DescriptionCancel


        #region DescriptionSave
        private void DescriptionSave()
        {
            // Set ReadOnly to True
            descriptionTextBox.IsReadOnly = true;
            articlesTreeView.Focus();
            // Hides Cancel and Save buttons, shows Edit
            buttonsManager.changeStates(new int[] { /**/-1, -1, -1, -1, /**/0, 0, 2, /**/-1, -1, /**/-1, -1, -1 });
            // Commit Changes to Database
            ViewModelBase article = (ViewModelBase)articlesTreeView.SelectedItem;
            int status;
            if (article is ArticleViewModel)
                status = ((ArticleViewModel)article).SaveDescription();
            else
                status = ((FolderViewModel)article).SaveDescription();

            if (status < (int)Utilities.errCode.NO_ERROR)
                this.StatusBarMessage("Unable to Save Description to Database. The Article or Folder may have been deleted.");
            // LOG THIS ACTION
        }
        #endregion //DescriptionSave


        #region DescriptionEdit
        private void DescriptionEdit()
        {
            // Make the textbox editable
            descriptionTextBox.IsReadOnly = false;
            // Hides this button, shows Cancel and Save
            buttonsManager.changeStates(new int[] { /**/-1, -1, -1, -1, /**/2, 2, 0, /**/-1, -1, /**/-1, -1, -1 });
            // This will change the appearance using a trigger
            descriptionTextBox.Focus();
        } 
        #endregion //DescriptionEdit


        #region Description MouseDoubleClick
        private void descriptionTextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            // TODO Add selecteditem check to avoid NullReferenceException
            if (articlesTreeView.SelectedItem != null)
            {
                if (descriptionTextBox.IsReadOnly)
                {
                    this.DescriptionEdit();
                    e.Handled = true;
                }
            }
        }
        #endregion //Description MouseDoubleClick




        #region linksListBox Hyperlink_Click
        private void Hyperlink_Click(object sender, RoutedEventArgs e)
        {
            var link = (Hyperlink)sender;
            var uri = link.NavigateUri.ToString();
            try
            {
                System.Diagnostics.Process.Start(uri);
            }
            catch (Exception ex)
            {
                Utilities.PrintError(ex);
            }
            e.Handled = true;
        }
        #endregion //linksListBox Hyperlink_Click




        #region filesListBox OpenFile Events (Double-Click, Enter) -----------------------------------------
        //---------------------------------------------------------------------------------------------------
        /*****
         * ---------- Files Stuff ----------
         * TODO FILESTREAM TESTING !!!
         */
        private void OpenSelectedFile(object sender)
        {
            ListBox lb = (ListBox)sender;
            try
            {
                if (lb.SelectedItems.Count > 0)
                {
                    // Get Unique FileID and retrieve file data from the database
                    FileViewModel fvm = (FileViewModel)lb.SelectedItem;
                    string filePath = Path.GetTempPath() + fvm.FileName;
                    byte[] fileData = Database.GetFileDataByID(fvm.FileID);
                    if (fileData == null)
                    {
                        this.StatusBarMessage("Unable to Open the File. It may have been deleted from the Database.");
                        // Reload this Article's Files list
                        ((ArticleViewModel)fvm.Parent).LoadFiles();
                    }
                    else
                    {
                        File.WriteAllBytes(filePath, fileData);
                        System.Diagnostics.Process.Start(filePath);
                    }
                }
            }
            catch (Exception e)
            {
                this.StatusBarMessage("Exception while opening the selected File.");
                Utilities.PrintError(e);
            }
        }
        private void filesListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            this.OpenSelectedFile(sender);
            e.Handled = true;
        }
        //---------------------------------------------------------------------------------------------------
        #endregion //filesListBox OpenFile Events (Double-Click, Enter) -------------------------------------


        #region filesListBox SaveAsFile Events (Ctrl+S, Menu Button) ----------------------------------------
        //---------------------------------------------------------------------------------------------------
        private void SaveAsSelectedFile(object sender)
        {
            ListBox lb = (ListBox)sender;
            try
            {
                if (lb.SelectedItems.Count > 0)
                {
                    System.Windows.Forms.SaveFileDialog saveDialog = new System.Windows.Forms.SaveFileDialog();

                    // Get Unique FileID and retrieve file data from the database
                    FileViewModel fvm = (FileViewModel)lb.SelectedItem;
                    
                    saveDialog.Title = "Please select the location to Save the file.";
                    saveDialog.Filter = "All files (*.*)|*.*";
                    saveDialog.FileName = fvm.FileName;
                    saveDialog.CheckPathExists = saveDialog.OverwritePrompt = saveDialog.ValidateNames = saveDialog.RestoreDirectory = true;

                    byte[] fileData = Database.GetFileDataByID(fvm.FileID);
                    if (fileData == null)
                    {
                        this.StatusBarMessage("Unable to Open the File. It may have been deleted from the Database.");
                        // Reload this Article's Files list
                        ((ArticleViewModel)fvm.Parent).LoadFiles();
                    }
                    else if (saveDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        // Write file to disk
                        File.WriteAllBytes(saveDialog.FileName, fileData);
                        //System.Diagnostics.Process.Start(Path.GetDirectoryName(saveDialog.FileName));
                    }
                }
            }
            catch (Exception e)
            {
                Utilities.PrintError(e);
            }
        }
        //---------------------------------------------------------------------------------------------------
        #endregion //filesListBox SaveAsFile Events (Ctrl+S, Menu Button) -----------------------------------


        #region filesListBox DeleteFile Events (Del, Menu Button) -------------------------------------------
        //---------------------------------------------------------------------------------------------------
        private void DeleteSelectedFile(object sender)
        {
            ListBox lb = (ListBox)sender;
            try
            {
                if (lb.SelectedItems.Count > 0)
                {
                    // Buffer Selected Index
                    int idx = lb.SelectedIndex;
                    // Get currently selected Article ViewModel
                    ArticleViewModel article = (ArticleViewModel)articlesTreeView.SelectedItem;
                    // Get currently selected File in the ListBox
                    FileViewModel selectedFile = (FileViewModel)lb.SelectedItem;
                    // Ask the Article to Delete the File
                    article.DeleteFile(selectedFile);
                    // Adjust index bounds
                    idx = (idx + 1 > lb.Items.Count) ? idx - 1 : idx;
                    // Select item at same position
                    lb.SelectedIndex = idx;
                }
            }
            catch (Exception e)
            {
                Utilities.PrintError(e);
            }
        }
        //---------------------------------------------------------------------------------------------------
        #endregion //filesListBox DeleteFile Events (Del, Menu Button) --------------------------------------


        #region filesListBox AddFile Events (DragDrop, Menu Button) -----------------------------------------
        //---------------------------------------------------------------------------------------------------
        private void AddFilesToArticle(string[] files)
        {
            foreach (var path in files)
            {
                // Dont Add Directories
                if (!Directory.Exists(path))
                {
                    FileInfo fInfo = new FileInfo(path);
                    // Get Currently Selected Article ViewModel
                    ArticleViewModel article = (ArticleViewModel)articlesTreeView.SelectedItem;
                    // Ask the ViewModel to Add the File
                    int sts = article.AddOrReplaceFile(fInfo);
                    if (sts < (int)Utilities.errCode.NO_ERROR)
                    {
                        this.StatusBarMessage("Unable to Add File to Database. The parent Article may have been deleted.");
                    }
                }
            }
        }
        private void filesListBox_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop, true))
            {
                string[] droppedFilePaths = (string[])e.Data.GetData(DataFormats.FileDrop, true);
                this.AddFilesToArticle(droppedFilePaths);
                e.Handled = true;
            }
        }
        //---------------------------------------------------------------------------------------------------
        #endregion //filesListBox AddFile Events (DragDrop, Menu Button) ------------------------------------
        



        #region SelectionChanged Handlers -------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------
        // Articles TreeView
        private void articlesTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            //object oldV = e.OldValue;
            object newV = e.NewValue;

            if (newV != null)
            {
                // Folder Selected
                if (newV is FolderViewModel)
                {
                    buttonsManager.changeStates(new int[] { /**/2, 2, 2, 2, /**/0, 0, 2, /**/1, 1, /**/1, 1, 1 });
                    filesListBox.AllowDrop = false;
                }
                // Article Selected
                else if (newV is ArticleViewModel)
                {
                    // Show delete button only if at least 1 link exist
                    int lnkShowDelete = (((ArticleViewModel)newV).Links.Count > 0) ? 2 : 1;
                    buttonsManager.changeStates(new int[] { /**/2, 2, 2, 2, /**/0, 0, 2, /**/lnkShowDelete, 2, /**/1, 1, 2 });
                    filesListBox.AllowDrop = true;
                }
            }
            else
            {
                // Nothing Selected
                buttonsManager.changeStates(new int[] { /**/1, 1, 1, 2, /**/0, 0, 1, /**/1, 1, /**/1, 1, 1 });
                filesListBox.AllowDrop = false;
            }
            e.Handled = true;
        }
        // Files ListBox
        private void filesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            //object oldV = (e.RemovedItems.Count > 0) ? e.RemovedItems[0] : null;
            object newV = (e.AddedItems.Count > 0) ? e.AddedItems[0] : null;

            if (newV != null)
            {
                // One File is Selected
                buttonsManager.changeStates(new int[] { /**/-1, -1, -1, -1, /**/-1, -1, -1, /**/-1, -1, /**/2, 2, -1 });
            }
            else
            {
                // Nothing Selected
                buttonsManager.changeStates(new int[] { /**/-1, -1, -1, -1, /**/-1, -1, -1, /**/-1, -1, /**/1, 1, -1 });
            }
            e.Handled = true;
        }
        //---------------------------------------------------------------------------------------------------
        #endregion //SelectionChanged Handlers --------------------------------------------------------------


        #region Clear SELECTION when clicking in the empty space --------------------------------------------
        //---------------------------------------------------------------------------------------------------
        private void articlesTreeView_MouseDown(object sender, MouseButtonEventArgs e)
        {
            TreeView tv = (TreeView)sender;
            ViewModelBase tvi = (ViewModelBase)tv.SelectedItem;
            if (tvi != null)
            {
                tvi.IsSelected = false;
                // Prevent a bug with selecting the previous item after clearing selection.
                tv.Focus();
                e.Handled = true;
            }
        }
        private void filesListBox_MouseDown(object sender, MouseButtonEventArgs e)
        {
            ((ListBox)sender).SelectedIndex = -1;
            e.Handled = true;
        }
        //---------------------------------------------------------------------------------------------------
        #endregion //Clear SELECTION when clicking in the empty space ---------------------------------------
        

        #region Keyboard Shortcut Handlers ------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------
        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.F5)
            {
                if (descriptionTextBox.IsReadOnly)
                {
                    object item = articlesTreeView.SelectedItem;
                    if (item != null)
                    {
                        ViewModelBase vmb = (ViewModelBase)item;
                        // Prevents data binding errors
                        vmb.IsSelected = false;
                    }
                    this.DataContext = new RootViewModel();
                    e.Handled = true;
                }
            }
        }
        private int delayFirst = 0;
        private const int delayMax = 500;
        private void descriptionTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (Keyboard.Modifiers == ModifierKeys.Control)
            {
                if (e.Key == Key.S)
                {
                    this.DescriptionSave();
                    e.Handled = true;
                }
            }
            else if (e.Key == Key.Escape)
            {
                if (delayFirst == 0)
                {
                    delayFirst = e.Timestamp;
                }
                else
                {
                    if (e.Timestamp - delayFirst <= delayMax)
                    {
                        delayFirst = 0;
                        // Call function
                        this.DescriptionCancel();
                    }
                    else
                    {
                        delayFirst = e.Timestamp;
                    }
                }
                e.Handled = true;
            }
        }
        private void filesListBox_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Enter:
                {
                    this.OpenSelectedFile(sender);
                    e.Handled = true;
                    break;
                }
                case Key.Delete:
                {
                    this.DeleteSelectedFile(sender);
                    e.Handled = true;
                    break;
                }
                case Key.S:
                {
                    // If Ctrl is Held
                    if (Keyboard.Modifiers == ModifierKeys.Control)
                    {
                        // SaveAsSelectedFile
                        this.SaveAsSelectedFile(filesListBox);
                        e.Handled = true;
                    }
                    break;
                }
                default:
                break;
            }
        }
        //---------------------------------------------------------------------------------------------------
        #endregion //Keyboard Shortcut Handlers -------------------------------------------------------------

            
        #region HeaderMenuButtons Click Handler -------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------
        private void HeaderButton_Click(object sender, RoutedEventArgs e)
        {
            HeaderMenuButton btn = (HeaderMenuButton)sender;
            switch (btn.Name)
            {
                // Articles Buttons
                case "buttonArticlesDelete":
                {
                    // Delete the currently selected Article or Folder
                    // Ask for Confirmation
                    // If Folder, Delete all Children Articles and Folders
                    // If Article, Delete all Children Links and Files
                    // LOG THIS ACTION and all childrens actions
                    break;
                }
                case "buttonArticlesRename":
                {
                    // Allow renaming of the currently selected Article or Folder
                    // Show editable TextBox, Focus on it
                    // Hide Header TextBlock
                    // Change background color to notify user
                    // Do not allow for 0 length string
                    // LOG THIS ACTION
                    break;
                }
                case "buttonArticlesNewArticle":
                {
                    // Get the currently selected item
                    object selectedItem = articlesTreeView.SelectedItem;
                    // Parent Folder
                    FolderViewModel parent;

                    // If nothing selected, Returns
                    if (selectedItem == null) return;

                    // Get the parent which we will add the item to
                    if (selectedItem is ArticleViewModel)
                        parent = (FolderViewModel)((ArticleViewModel)selectedItem).Parent;
                    else
                        parent = (FolderViewModel)selectedItem;

                    // Create and Show Add Article Dialog
                    AddArticleWindow addArticle = new AddArticleWindow(parent.FolderName);
                    // Make child of current window
                    addArticle.Owner = this;
                    // Display dialog
                    addArticle.ShowDialog();
                    // We clicked OK
                    if (addArticle.DialogResult == true)
                    {
                        // Add the Article to Parent Folder
                        parent.AddArticle(addArticle.ArticleName);
                        parent.IsExpanded = true;
                    }
                    // LOG THIS ACTION
                    break;
                }
                case "buttonArticlesNewFolder":
                {
                    // Creates a new Folder under the currently selected folder
                    // If NO selection, Create Folder on Root.
                    
                    // Get the currently selected item
                    object selectedItem = articlesTreeView.SelectedItem;
                    // Parent Folder
                    FolderViewModel parent;

                    // Get the parent which we will add the item to
                    if (selectedItem == null)
                        parent = null;
                    else if (selectedItem is ArticleViewModel)
                        parent = (FolderViewModel)((ArticleViewModel)selectedItem).Parent;
                    else
                        parent = (FolderViewModel)selectedItem;

                    // Create and Show Add Article Dialog
                    AddArticleWindow addFolder = new AddArticleWindow((parent == null) ? "Root" : parent.FolderName, true);
                    // Make child of current window
                    addFolder.Owner = this;
                    // Display dialog
                    addFolder.ShowDialog();
                    // We clicked OK
                    if (addFolder.DialogResult == true)
                    {
                        // Add the Article to Parent Folder
                        // If nothing selected, Parent is RootViewModel
                        if (parent == null)
                            ((RootViewModel)this.DataContext).AddFolder(addFolder.ArticleName);
                        else
                        {
                            parent.AddFolder(addFolder.ArticleName);
                            parent.IsExpanded = true;
                        }
                    }
                    // LOG THIS ACTION
                    break;
                }



                // Description Buttons
                case "buttonDescriptionCancel":
                {
                    this.DescriptionCancel();
                    // LOG THIS ACTION
                    break;
                }
                case "buttonDescriptionSave":
                {
                    this.DescriptionSave();
                    break;
                }
                case "buttonDescriptionEdit":
                {
                    this.DescriptionEdit();
                    break;
                }



                // Links Buttons
                case "buttonLinksDelete":
                {
                    // Create a list to hold the Links to be deleted
                    List<LinkViewModel> toDelete = new List<LinkViewModel>();
                    foreach (LinkViewModel link in linksListBox.Items)
                    {
                        if (link.IsChecked)
                        {
                            // Add to Checked List
                            toDelete.Add(link);
                        }
                    }
                    // We got something to Delete
                    if (toDelete.Count > 0)
                    {
                        ArticleViewModel article = (ArticleViewModel)articlesTreeView.SelectedItem;
                        article.DeleteLinks(toDelete);
                        if (linksListBox.Items.Count <= 0)
                        {
                            buttonsManager.changeStates(new int[] { /**/-1, -1, -1, -1, /**/-1, -1, -1, /**/1, -1, /**/-1, -1, -1 });
                        }
                    }
                    break;
                }
                case "buttonLinksAdd":
                {
                    // Create and Show AddLink Dialog
                    AddLinkWindow addLink = new AddLinkWindow();
                    addLink.Owner = this;
                    // Display dialog
                    addLink.ShowDialog();
                    // We clicked OK
                    if (addLink.DialogResult == true)
                    {
                        // Get current article
                        ArticleViewModel article = (ArticleViewModel)articlesTreeView.SelectedItem;
                        // Add link to article
                        int sts = article.AddLink(addLink.LinkName, addLink.LinkUri);
                        if (sts < (int)Utilities.errCode.NO_ERROR)
                        {
                            this.StatusBarMessage("Unable to Add Link to Database. The parent Article may have been deleted.");
                        }
                        else if (linksListBox.Items.Count > 0)
                        {
                            buttonsManager.changeStates(new int[] { /**/-1, -1, -1, -1, /**/-1, -1, -1, /**/2, -1, /**/-1, -1, -1 });
                        }
                    }
                    // LOG THIS ACTION
                    break;
                }



                // Files Buttons
                case "buttonFilesSaveAs":
                {
                    // Save the current file locally
                    this.SaveAsSelectedFile(filesListBox);
                    // LOG THIS ACTION
                    break;
                }
                case "buttonFilesDelete":
                {
                    // Delete the currently selected file
                    this.DeleteSelectedFile(filesListBox);
                    // LOG THIS ACTION
                    break;
                }
                case "buttonFilesAdd":
                {
                    // Open a Browse File Dialog that allows Multiple Selections
                    System.Windows.Forms.OpenFileDialog openDialog = new System.Windows.Forms.OpenFileDialog();
                    openDialog.Title = "Select file(s) to add to the Knowledge Base (Maximum 30 Mb per file).";
                    openDialog.Multiselect = true;
                    openDialog.Filter = "All files (*.*)|*.*";
                    openDialog.CheckPathExists = openDialog.ValidateNames = openDialog.RestoreDirectory = true;
                    if (openDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        this.AddFilesToArticle(openDialog.FileNames);
                    }
                    // LOG THIS ACTION
                    break;
                }
                default:
                break;
            }
            e.Handled = true;
        }
        //---------------------------------------------------------------------------------------------------
        #endregion //HeaderMenuButtons Click Handler --------------------------------------------------------
    }
}
